home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / os2 / gnuwget.zip / wget-1.4.3 / src / init.c < prev    next >
C/C++ Source or Header  |  1997-02-09  |  23KB  |  866 lines

  1. /* Reading/parsing the initialization file.
  2.    Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
  3.    
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2 of the License, or
  7.    (at your option) any later version.
  8.    
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.    
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18.  
  19. #ifdef HAVE_CONFIG_H
  20. #  include <config.h>
  21. #endif /* HAVE_CONFIG_H */
  22.  
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <stdlib.h>
  28. #ifdef HAVE_UNISTD_H
  29. #  include <unistd.h>
  30. #endif
  31. #ifdef HAVE_STRING_H
  32. #  include <string.h>
  33. #else
  34. #  include <strings.h>
  35. #endif
  36.  
  37. #ifdef HAVE_PWD_H
  38. #include <pwd.h>
  39. #endif
  40.  
  41. #include <assert.h>
  42.  
  43. #include "wget.h"
  44. #include "options.h"
  45. #include "utils.h"
  46. #include "init.h"
  47. #include "url.h"
  48. #include "ftp.h"
  49. #include "host.h"
  50. #include "recur.h"
  51. #include "netrc.h"
  52.  
  53. extern char *version_string;
  54. extern struct options opt;
  55. extern int errno;
  56.  
  57. extern acc_t *netrc_list;
  58.  
  59. /* List of recognized commands, each consisting of a string-name, ID
  60.    and what_to_do.
  61.  
  62.    When adding a new command, you must append it to the list (it is a
  63.    good idea to keep the list sorted).  Each command has an ID (as
  64.    listed in init.h, a pointer over which it will work, and an
  65.    indication how it will work.
  66.  
  67.    CONOFF  -> integer toggle
  68.    CNUM    -> integer
  69.    CNUMINF -> integer, with "inf" synomim for 0
  70.    CSTR    -> free old string, if non-NULL, and strdup val
  71.    CVEC    -> vector
  72.    CSPEC   -> special, has to be treated specially in setval */
  73. struct cmd commands[] = {
  74.    { "accept",        ACCEPT,        &opt.accepts,        CVEC },
  75.    { "addhostdir",    ADDHOSTDIR,    &opt.add_hostdir,    CONOFF },
  76.    { "alwaysrest",    ALWAYSREST,    &opt.always_rest,    CONOFF },
  77.    { "backups",        BACKUPS,    &opt.backups,        CONOFF },
  78.    { "basehref",    BASEHREF,    &opt.base_href,        CSTR },
  79.    { "cache",        CACHE,        &opt.proxy_cache,    CONOFF },
  80.    { "convertlinks",    CONVERTLINKS,    &opt.convert_links,    CONOFF },
  81.    { "debug",        DEBUG_,        NULL,            CSPEC },
  82.    { "deleteafter",    DELETEAFTER,    &opt.delete_after,    CONOFF },
  83.    { "dirmode",        DIRMODE,    NULL,            CSPEC },
  84.    { "dirprefix",    DIRPREFIX,    &opt.dir_prefix,    CSTR },
  85.    { "dirstruct",    DIRSTRUCT,    NULL,            CSPEC },
  86.    { "domains",        DOMAINS,    &opt.domains,        CVEC },
  87.    { "dotbytes",    DOTBYTES,    &opt.dot_bytes,        CBYT },
  88.    { "dotsinline",    DOTSINLINE,    &opt.dots_in_line,    CNUM },
  89.    { "dotspacing",    DOTSPACING,    &opt.dot_spacing,    CNUM },
  90.    { "dotstyle",    DOTSTYLE,    NULL,            CSPEC },
  91.    { "excludedirectories", EXCLUDEDIRECTORIES,    &opt.excludes,    CVECDIR },
  92.    { "excludedomains",    EXCLUDEDOMAINS,    &opt.exclude_domains,    CVEC },
  93.    { "followftp",    FOLLOWFTP,    &opt.follow_ftp,    CONOFF },
  94.    { "forcehtml",    FORCEHTML,    &opt.force_html,    CONOFF },
  95.    { "ftpproxy",    FTPPROXY,    &opt.ftp_proxy,        CSTR },
  96.    { "glob",        GLOB,        &opt.ftp_glob,        CONOFF },
  97.    { "header",        HEADER,        NULL,            CSPEC },
  98.    { "htmlify",        HTMLIFY,    NULL,            CSPEC },
  99.    { "httppasswd",    HTTPPASSWD,    &opt.http_passwd,    CSTR },
  100.    { "httpproxy",    HTTPPROXY,    &opt.http_proxy,    CSTR },
  101.    { "httpuser",    HTTPUSER,    &opt.http_user,        CSTR },
  102.    { "ignorelength",    IGNORELENGTH,    &opt.ignore_length,    CONOFF },
  103.    { "includedirectories", INCLUDEDIRECTORIES, &opt.includes,    CVECDIR },
  104.    { "input",        INPUT,        &opt.input_filename,    CSTR },
  105.    { "killlonger",    KILLLONGER,    &opt.kill_longer,    CONOFF },
  106.    { "logfile",        LOGFILE,    &opt.lfilename,        CSTR },
  107.    { "login",        LOGIN,        &opt.ftp_acc,        CSTR },
  108.    { "mirror",        MIRROR,        NULL,            CSPEC },
  109.    { "noclobber",    NOCLOBBER,    &opt.noclobber,        CONOFF },
  110.    { "noparent",    NOPARENT,    &opt.no_parent,        CONOFF },
  111.    { "noproxy",        NOPROXY,    &opt.no_proxy,        CVEC },
  112.    { "numtries",    NUMTRIES,    &opt.ntry,        CNUMINF },
  113.    { "outputdocument",    OUTPUTDOCUMENT, NULL,            CSPEC },
  114.    { "passiveftp",    PASSIVEFTP,    &opt.ftp_pasv,        CONOFF },
  115.    { "passwd",        PASSWD,        &opt.ftp_pass,        CSTR },
  116.    { "quiet",        QUIET,        &opt.quiet,        CONOFF },
  117.    { "quota",        QUOTA,        &opt.quota,        CBYT },
  118.    { "reclevel",    RECLEVEL,    &opt.maxreclevel,    CNUMINF },
  119.    { "recursive",    RECURSIVE,    NULL,            CSPEC },
  120.    { "reject",        REJECT,        &opt.rejects,        CVEC },
  121.    { "relativeonly",    RELATIVEONLY,    &opt.relative_only,    CONOFF },
  122.    { "removelisting",    REMOVELISTING,    &opt.remove_listing,    CONOFF },
  123.    { "retrsymlinks",    RETRSYMLINKS,    &opt.retr_symlinks,    CONOFF },
  124.    { "robots",        ROBOTS,        &opt.use_robots,    CONOFF },
  125.    { "saveheaders",    SAVEHEADERS,    &opt.save_headers,    CONOFF },
  126.    { "serverresponse",    SERVERRESPONSE, &opt.server_response,    CONOFF },
  127.    { "simplehostcheck", SIMPLEHOSTCHECK,&opt.simple_check,    CONOFF },
  128.    { "spanhosts",    SPANHOSTS,    &opt.spanhost,        CONOFF },
  129.    { "spider",        SPIDER,        &opt.spider,        CONOFF },
  130.    { "timeout",        TIMEOUT,    NULL,            CSPEC },
  131.    { "timestamping",    TIMESTAMPING,    &opt.timestamping,    CONOFF },
  132.    { "useproxy",    USEPROXY,    &opt.use_proxy,        CONOFF },
  133.    { "useragent",    USERAGENT,    &opt.useragent,        CSTR },
  134.    { "verbose",        VERBOSE,    &opt.verbose,        CONOFF },
  135.    { "wait",        WAIT,        &opt.wait,        CNUM },
  136.    { NULL, LAST }
  137. };
  138.  
  139.  
  140. /* Return index of a valid command, -1 if none. */
  141. int
  142. comind(const struct cmd *commands, const char *com)
  143. {
  144.    int i;
  145.  
  146.    for (i = 0; commands[i].name; i++)
  147.       if (!strcasecmp(commands[i].name, com))
  148.      return i;
  149.    return -1;
  150. }
  151.  
  152. /* Reset the variables to default values. */
  153. void
  154. defaults(void)
  155. {
  156.    char *tmp;
  157.    
  158. #ifdef DEBUG
  159.    opt.debug = 0;
  160. #endif
  161.    opt.verbose = -1;
  162.    opt.quiet = 0;
  163.    
  164.    opt.kill_longer = 0;
  165.    opt.ignore_length = 0;
  166.    opt.lfilename = NULL;
  167.    opt.input_filename = NULL;
  168.    opt.dirstruct = 0;
  169.    opt.no_dirstruct = 0;
  170.    opt.dir_prefix = nstrdup(DEFAULT_DIRPREFIX);
  171.    
  172.    opt.ntry = DEFAULT_NUMBER_OF_TRIES;
  173.    opt.recursive = 0;
  174.    opt.maxreclevel = DEFAULT_MAXRECLEVEL;
  175.    opt.spanhost = 0;
  176.    opt.relative_only = 0;
  177.    opt.no_parent = 0;
  178.    opt.accepts = opt.rejects = NULL;
  179.    opt.includes = opt.excludes = NULL;
  180.    
  181.    opt.simple_check = 0;
  182.    opt.dirmode = DEFAULT_DIRMODE;
  183.    opt.add_hostdir = 1;
  184.    opt.noclobber = 0;
  185.  
  186.    opt.follow_ftp = 0;
  187.    opt.retr_symlinks = 0;
  188.    opt.always_rest = 0;
  189.    opt.ftp_acc = nstrdup(DEFAULT_FTP_ACCT);
  190.    opt.ftp_pass = nstrdup(ftp_getaddress());
  191.    opt.ftp_glob = 1;
  192.    opt.ftp_pasv = 0;
  193.    opt.htmlify = 1;
  194.  
  195.    opt.http_user = NULL;
  196.    opt.http_passwd = NULL;
  197.    opt.user_header = NULL;
  198.  
  199.    opt.useragent = NULL;
  200.    opt.output_document = NULL;
  201.  
  202.    opt.use_proxy = 1;
  203.    opt.http_proxy = opt.ftp_proxy = NULL;
  204.    opt.no_proxy = NULL;
  205.    tmp = getenv("no_proxy");
  206.    if (tmp)
  207.       opt.no_proxy = sepstring(tmp);
  208.    opt.proxy_cache = 1;
  209.    opt.base_href = NULL;
  210.  
  211. #ifdef HAVE_SELECT
  212.    opt.timeout = DEFAULT_TIMEOUT;
  213. #endif
  214.    opt.wait = 0;
  215.  
  216.    opt.use_robots = 1;
  217.  
  218.    opt.quota = 0;
  219.    opt.downloaded = 0;
  220.    opt.numurls = 0;
  221.  
  222.    opt.server_response = 0;
  223.    opt.save_headers = 0;
  224.    opt.timestamping = 0;
  225.    opt.remove_listing = 1;
  226.    opt.delete_after = 0;
  227.  
  228.    opt.backups = 0;
  229.    opt.convert_links = 0;
  230.  
  231.    opt.dot_bytes = DOT_BYTES;
  232.    opt.dot_spacing = DOT_SPACING;
  233.    opt.dots_in_line = DOTS_IN_LINE;
  234. }
  235.  
  236. /* Return the user's home directory (strdup-ed), or NULL if none found. */
  237. char *
  238. home_dir(void)
  239. {
  240.    char *home;
  241. #ifdef WINDOWS
  242. /* TODO - maybe I should grab home_dir from registry,
  243.    but the best that I could get from there is users
  244.     Start menu. It sucks! */
  245.         home = getenv("HOME");
  246.     return home ? nstrdup(home) : NULL;
  247.  
  248. #else /* Unix or stuff */
  249.    struct passwd *pwd;
  250.  
  251.    home = getenv("HOME");
  252.    /* If HOME is not defined, try getting it from the password file. */
  253.    if (!home)
  254.    {
  255.       pwd = getpwuid(getuid());
  256.       if (!pwd || !pwd->pw_dir)
  257.      return NULL;
  258.       home = pwd->pw_dir;
  259.    }
  260.    if (home)
  261.       return nstrdup(home);
  262.    return NULL;
  263. #endif /* WINDOWS */
  264. }
  265.  
  266. /* Return the path to the user's .wgetrc. */
  267. char *
  268. init_path(void)
  269. {
  270.    char *env, *home, *path;
  271.    struct stat buf;
  272.    int err;
  273.  
  274.    /* Try the environment. */
  275.    if ((env = getenv("WGETRC")))
  276.    {
  277.       path = nstrdup(env);
  278.       err = stat(path, &buf);
  279.       if (err == 0)
  280.      return path;
  281.       free(path);
  282.       fprintf(stderr, "Cannot load .wgetrc.\n");
  283.       exit(1);
  284.    }
  285.    /* If that failed, try $HOME/.wgetrc. */
  286.    home = home_dir();
  287.    if (!home)
  288.       return NULL;
  289.    path = (char *)nmalloc(strlen(home) + 1 + strlen(".wgetrc") + 1);
  290.    sprintf(path, "%s/.wgetrc", home);
  291.    free(home);
  292.    err = stat(path, &buf);
  293.    if (err == -1)
  294.    {
  295.       free(path);
  296.       return NULL;
  297.    }
  298.    return path;
  299. }
  300.  
  301. /* Initialize variables from a wgetrc file */
  302. void
  303. run_wgetrc(const char *path)
  304. {
  305.    char *com, *val;
  306.    FILE *fp;
  307.    char *line;
  308.    int stat, ln, length;
  309.  
  310.    fp = fopen(path, "r");
  311.    if (!fp)
  312.    {
  313.       fprintf(stderr, "Cannot read %s (%s).\n", path, mystrerror(errno));
  314.       return;
  315.    }
  316.    /* Reset line number. */
  317.    ln = 1;
  318.    /* While there are lines in the file: */
  319.    while ((line = read_whole_line(fp)))
  320.    {
  321.       length = strlen(line);
  322.       if (length && line[length - 1] == '\r')
  323.      line[length - 1] = '\0';
  324.       /* Parse the line. */
  325.       stat = parse_line((unsigned char *)line,
  326.             (unsigned char **)&com, (unsigned char **)&val);
  327.       free(line);
  328.       /* If everything is OK, set the value. */
  329.       if (stat == 1)
  330.       {
  331.      if (!setval(com, val))
  332.         fprintf(stderr, "Error in %s at line %d.\n", path, ln);
  333.      free(com);
  334.      free(val);
  335.       }
  336.       else if (stat == 0)
  337.      fprintf(stderr, "Error in %s at line %d.\n", path, ln);
  338.       ++ln;
  339.    }
  340.    fclose(fp);
  341. }
  342.  
  343. /* Initialize the defaults and run the system wgetrc and user's own
  344.    wgetrc.  Parse ~/.netrc. */
  345. void
  346. initialize(void)
  347. {
  348.    char *path;
  349.    struct stat buf;
  350.    int err;
  351.    char *home;
  352.  
  353.    /* Load defaults. */
  354.    defaults();
  355.  
  356.    /* If SYSTEM_WGETRC is defined, use it. */
  357. #ifdef SYSTEM_WGETRC
  358.    err = stat(SYSTEM_WGETRC, &buf);
  359.    if (err != -1) /* File exists */
  360.       run_wgetrc(SYSTEM_WGETRC);
  361. #endif
  362.    /* Override it with your own, if one exists. */
  363.    path = init_path();
  364.    if (path)
  365.    {
  366.       run_wgetrc(path);
  367.       free(path);
  368.    }
  369.    /* Find ~/.netrc. */
  370.    netrc_list = NULL;
  371.    home = home_dir();
  372.    if (home)
  373.    {
  374.       path = nmalloc(strlen(home) + 1 + strlen(NETRC_FILE_NAME) + 1);
  375.       sprintf(path, "%s/%s", home, NETRC_FILE_NAME);
  376.       free(home);
  377.       err = stat(path, &buf);
  378.       if (err == 0)
  379.      netrc_list = parse_netrc(path);
  380.       free(path);
  381.    }
  382. }
  383.  
  384. /* Parse the line pointed by line, with the syntax:
  385.    <sp>* command <sp>* = <sp>* value <newline>
  386.    Uses malloc to allocate space for command and value.
  387.    If the line is invalid, data is freed and 0 is returned.
  388.  
  389.    Return values:
  390.     1 - success
  391.     0 - failure
  392.    -1 - comment */
  393. int
  394. parse_line(const unsigned char *line, unsigned char **com, unsigned char **val)
  395. {
  396.    int i, j;
  397.  
  398.    /* Skip spaces. */
  399.    for (i = 0; line[i] == ' ' || line[i] == '\t'; i++);
  400.  
  401.    /* Skip empty or hashed lines. */
  402.    if (!line[i] || line[i] == '\n' || line[i] == '#')
  403.       return -1;
  404.    
  405.    /* Allocate command. */
  406.    *com = (unsigned char *)nmalloc(1);
  407.    (*com)[0] = '\0';
  408.    for (j = 0; (isalpha(line[i]) || line[i] == '_' || line[i] == '-'); i++)
  409.    {
  410.       if (line[i] == '_' || line[i] == '-')
  411.      continue;
  412.       *com = (unsigned char *)nrealloc(*com, j + 2);
  413.       (*com)[j] = tolower(line[i]);
  414.       (*com)[j + 1] = '\0';
  415.       ++j;
  416.    }
  417.    /* The next char should be space or '='. */
  418.    if (!isspace(line[i]) && (line[i] != '='))
  419.    {
  420.       free(*com);
  421.       return 0;
  422.    }
  423.    /* If the command is invalid, exit now. */
  424.    if (comind(commands, (char *)*com) == NONE)
  425.    {
  426.       free(*com);
  427.       return 0;
  428.    }
  429.    /* Skip spaces before '='. */
  430.    for (; isspace(line[i]); i++);
  431.    /* If '=' not found, bail out. */
  432.    if (line[i] != '=')
  433.    {
  434.       free(com);
  435.       return 0;
  436.    }
  437.    /* Skip spaces after '='. */
  438.    for (++i; isspace(line[i]); i++);
  439.    /* Get the ending position. */
  440.    for (j = i; line[i] && line[i] != '\n'; i++);
  441.    /* Allocate *val, and copy from line. */
  442.    (*val) = (unsigned char *)nmalloc(i - j + 1);
  443.    strncpy((char *)*val, (char *)line + j, i - j);
  444.    (*val)[i - j] = '\0';
  445.    return 1;
  446. }
  447.  
  448. /* Do the job for each line.  No fatals -- error signal prints a
  449.    warning and resets to default value.  All error messages are
  450.    printed to stderr, *not* to opt.lfile, since opt.lfile wasn't even
  451.    generated yet.  */
  452. int
  453. setval(const char *com, const char *val)
  454. {
  455.    int flag, mirror;
  456.    int i, ind;                  /* To be used for various loops. */
  457.    const char *p;               /* Likewise. */
  458.  
  459.    if (!com || !val)
  460.       return 0;
  461.    ind = comind(commands, com);
  462.    if (ind == -1)
  463.    {
  464. #ifdef DEBUG
  465.       fprintf(stderr, "Unknown command `%s', value `%s'.\n", com, val);
  466. #endif
  467.       return 0;
  468.    }
  469.    switch (commands[ind].what_to_do)
  470.    {
  471.       case CONOFF:
  472.      return setonoff((int *)commands[ind].varp, com, val);
  473.      break;
  474.       case CNUM:
  475.      return setnum((int *)commands[ind].varp, com, val, 0);
  476.      break;
  477.       case CNUMINF:
  478.      return setnum((int *)commands[ind].varp, com, val, SINF);
  479.      break;
  480.       case CSTR:
  481.      if (*(char **)commands[ind].varp)
  482.         free(*(char **)commands[ind].varp);
  483.      *(char **)commands[ind].varp = (char *)nstrdup(val);
  484.      break;
  485.       case CVEC:
  486.      if (*val)
  487.         *(char ***)commands[ind].varp = merge_vecs(*(char ***)commands[ind].varp,
  488.                              sepstring(val));
  489.      else
  490.      {
  491.         free_vec(*(char ***)commands[ind].varp);
  492.         *(char ***)commands[ind].varp = NULL;
  493.      }
  494.      break;
  495.       case CVECDIR:
  496.      if (*val)
  497.      {
  498.         /* Strip the trailing slashes from directories. */
  499.         char **t, **seps;
  500.         int len;
  501.  
  502.         seps = sepstring(val);
  503.         for (t = seps; t && *t; t++)
  504.         {
  505.            len = strlen(*t);
  506.            /* Skip degenerate case of root directory. */
  507.            if (len > 1)
  508.            {
  509.           if ((*t)[len - 1] == '/')
  510.              (*t)[len - 1] = '\0';
  511.            }
  512.         }
  513.         *(char ***)commands[ind].varp =
  514.            merge_vecs(*(char ***)commands[ind].varp, seps);
  515.      }
  516.      else
  517.      {
  518.         free_vec(*(char ***)commands[ind].varp);
  519.         *(char ***)commands[ind].varp = NULL;
  520.      }
  521.      break;
  522.       case CBYT:
  523.      return setbytes((long *)commands[ind].varp, com, val);
  524.      break;
  525.       case CSPEC:
  526.      switch(commands[ind].id)
  527.      {
  528.         case DEBUG_:
  529. #ifdef DEBUG
  530.            return setonoff(&opt.debug, com, val);
  531. #else
  532.            fprintf(stderr, "%s: debug support not loaded.\n", com);
  533.            return 0;
  534. #endif
  535.            break;
  536.         case DIRMODE:
  537.            flag = getperms(val);
  538.            if (flag == -1)
  539.            {
  540.           fprintf(stderr, "%s: Invalid mode specification `%s'.\n",
  541.               com, val);
  542.           return 0;
  543.            }
  544.            opt.dirmode = DEFAULT_DIRMODE;
  545.            break;
  546.         case DIRSTRUCT:
  547.            if (!setonoff(&opt.dirstruct, com, val))
  548.           return 0;
  549.            /* Since dirstruct behaviour is explicitly changed,
  550.           no_dirstruct must be affected inversely. */
  551.            if (opt.dirstruct)
  552.           opt.no_dirstruct = 0;
  553.            else
  554.           opt.no_dirstruct = 1;
  555.            break;
  556.         case DOTSTYLE:
  557.            /* Retrieval styles. */
  558.            if (!strcasecmp(val, "default"))
  559.            {
  560.           /* Default style: 1K dots, 10 dots in a cluster, 50
  561.              dots in a line. */
  562.           opt.dot_bytes = DOT_BYTES;
  563.           opt.dot_spacing = DOT_SPACING;
  564.           opt.dots_in_line = DOTS_IN_LINE;
  565.            }
  566.            else if (!strcasecmp(val, "binary"))
  567.            {
  568.           /* "Binary" retrieval: 8K dots, 16 dots in a cluster,
  569.              64 dots (512K) in a line. */
  570.           opt.dot_bytes = 8192;
  571.           opt.dot_spacing = 16;
  572.           opt.dots_in_line = 64;
  573.            }
  574.            else if (!strcasecmp(val, "mega"))
  575.            {
  576.           /* "Mega" retrieval, for retrieving very long files;
  577.              each dot is 64K, 8 dots in a cluster, 6 clusters
  578.              (3M) in a line.  */
  579.           opt.dot_bytes = 65536L;
  580.           opt.dot_spacing = 8;
  581.           opt.dots_in_line = 48;
  582.            }
  583.            else if (!strcasecmp(val, "micro"))
  584.            {
  585.           /* "Micro" retrieval, for retrieving very small files;
  586.              each dot is 128 bytes, 8 dots in a cluster, 6
  587.              clusters (6K) in a line.  */
  588.           opt.dot_bytes = 128;
  589.           opt.dot_spacing = 8;
  590.           opt.dots_in_line = 48;
  591.            }
  592.            else
  593.            {
  594.           fprintf(stderr, "%s: Invalid specification `%s'.\n", com, val);
  595.           return 0;
  596.            }
  597.            break;
  598.         case HEADER:
  599.            if (!*val)
  600.            {
  601.           /* Empty header means to reset headers. */
  602.           if (opt.user_header)
  603.           {
  604.              free(opt.user_header);
  605.              opt.user_header = NULL;
  606.           }
  607.            }
  608.            else
  609.            {
  610.           /* Check for sanity of the header value. */
  611.           flag = 0;
  612.           for (p = val; *p && *p != ':' && !isspace(*p); p++);
  613.           /* The header MUST contain ':' preceded by at least one
  614.              non-space character. */
  615.           if (*p != ':' || p == val)
  616.              flag = 1;
  617.           /* The header MUST NOT contain newlines. */
  618.           if (!flag)
  619.              if (strchr(val, '\n'))
  620.             flag = 1;
  621.           if (flag)
  622.           {
  623.              fprintf(stderr, "%s: Invalid specification `%s'.\n", com, val);
  624.              return 0;
  625.           }
  626.           i = opt.user_header ? strlen(opt.user_header) : 0;
  627.           opt.user_header = (char *)nrealloc(opt.user_header,
  628.                              i + strlen(val) + 2 + 1);
  629.           strcpy(opt.user_header + i, val);
  630.           i += strlen(val);
  631.           opt.user_header[i++] = '\r';
  632.           opt.user_header[i++] = '\n';
  633.           opt.user_header[i] = '\0';
  634.            }
  635.            break;
  636.         case HTMLIFY:
  637.            flag = setonoff(&opt.htmlify, com, val);
  638.            if (flag && !opt.htmlify)
  639.           opt.remove_listing = 0;
  640.            return flag;
  641.            break;
  642.         case MIRROR:
  643.            if (!setonoff(&mirror, com, val))
  644.           return 0;
  645.            if (mirror)
  646.            {
  647.           opt.recursive = 1;
  648.           if (!opt.no_dirstruct)
  649.              opt.dirstruct = 1;
  650.           opt.timestamping = 1;
  651.           opt.maxreclevel = 0;
  652.           opt.remove_listing = 0;
  653.            }
  654.            break;
  655.         case OUTPUTDOCUMENT:
  656.            if (opt.output_document)
  657.           free(opt.output_document);
  658.            opt.output_document = nstrdup(val);
  659.            opt.ntry = 1;
  660.            if (ISHYPHEN(opt.output_document))
  661.           opt.quiet = 1;
  662.            break;
  663.         case RECURSIVE:
  664.            if (!setonoff(&opt.recursive, com, val))
  665.           return 0;
  666.            else
  667.            {
  668.           if (opt.recursive && !opt.no_dirstruct)
  669.              opt.dirstruct = 1;
  670.            }
  671.            break;
  672.         case TIMEOUT:
  673. #ifdef HAVE_SELECT
  674.            return setnum(&opt.timeout, com, val, SINF);
  675. #else
  676.            fprintf(stderr, "Unable to set timeout without select().\n");
  677. #endif
  678.            break;
  679.         default:
  680.            assert(0);
  681.      }
  682.      break;
  683.       default:
  684.      assert(0);
  685.    }
  686.    return 1;
  687. }
  688.  
  689. /* Return 0 if off, 1 if on, -1 if error. */
  690. int
  691. onoff(const char *val)
  692. {
  693.    if (strcasecmp(val, "on") == 0 || strcmp(val, "1") == 0)
  694.       return 1;
  695.    else if (strcasecmp(val, "off") == 0 || strcmp(val, "0") == 0)
  696.       return 0;
  697.    else
  698.       return -1;
  699. }
  700.  
  701. /* Set the integer value pointed to by what on value represented in
  702.    string val. String com is used only for error messages. */
  703. int
  704. setonoff(int *what, const char *com, const char *val)
  705. {
  706.    int flag;
  707.  
  708.    flag = onoff(val);
  709.    if (flag == -1)
  710.    {
  711.       fprintf(stderr, "%s: Please specify on or off.\n", com);
  712.       return 0;
  713.    }
  714.    *what = flag;
  715.    return 1;
  716. }
  717.  
  718. /* Similar to setonoff, but converts numbers.  With incorrect
  719.    specification, the number remains unchanged.  With flags set to
  720.    SINF, uses "inf" for meaning zero.  */
  721. int
  722. setnum(int *what, const char *com, const char *val, int flags)
  723. {
  724.    int res;
  725.  
  726.    if (flags == SINF && !strcasecmp(val, "inf"))
  727.    {
  728.       *what = 0;
  729.       return 1;
  730.    }
  731.    res = myatoi(val);
  732.    if (res == -1)
  733.    {
  734.       fprintf(stderr, "%s: Invalid specification `%s'.\n", com, val);
  735.       return 0;
  736.    }
  737.    *what = res;
  738.    return 1;
  739. }
  740.  
  741. /* Return the int value of a positive integer written in a string,
  742.    or -1 if an error was encountered */
  743. int
  744. myatoi(const char *s)
  745. {
  746.    int res;
  747.    const char *orig;
  748.  
  749.    orig = s;
  750.    for (res = 0; *s && isdigit(*s); s++)
  751.    {
  752.       res *= 10;
  753.       res += *s - '0';
  754.    }
  755.    if (*s || orig == s)
  756.       return -1;
  757.    else
  758.       return res;
  759. }
  760.  
  761. /* Return the decimal representation of octal mode three-digit
  762.    permission string, or -1 if incorrect. */
  763. int
  764. getperms(const char *s)
  765. {
  766.    if (strlen(s) != 3 || !ISODIGIT(s[0]) || !ISODIGIT(s[1]) || !ISODIGIT(s[2]))
  767.       return -1;
  768.    return (s[2] - '0') + 8 * (s[1] - '0') + 64 * (s[0] - '0');
  769. }
  770.  
  771. /* Free the memory allocated by global variables. */
  772. void
  773. cleanup(void)
  774. {
  775.    recursive_retrieve(NULL, NULL, RCLEANUP);
  776.    clean_hosts();
  777.    free_netrc(netrc_list);
  778.    if (opt.dfp)
  779.       fclose(opt.dfp);
  780.    if (opt.lfilename)
  781.       free(opt.lfilename);
  782.    free(opt.dir_prefix);
  783.    if (opt.input_filename)
  784.       free(opt.input_filename);
  785.    if (opt.output_document)
  786.       free(opt.output_document);
  787.    free_vec(opt.accepts);
  788.    free_vec(opt.rejects);
  789.    free_vec(opt.excludes);
  790.    free_vec(opt.includes);
  791.    free_vec(opt.domains);
  792.    free(opt.ftp_acc);
  793.    free(opt.ftp_pass);
  794.    if (opt.ftp_proxy)
  795.       free(opt.ftp_proxy);
  796.    if (opt.http_proxy)
  797.       free(opt.http_proxy);
  798.    free_vec(opt.no_proxy);
  799.    if (opt.useragent)
  800.       free(opt.useragent);
  801.    if (opt.http_user)
  802.       free(opt.http_user);
  803.    if (opt.http_passwd)
  804.       free(opt.http_passwd);
  805.    if (opt.user_header)
  806.       free(opt.user_header);
  807. }
  808.  
  809. /* Sets the value stored in string val to a long int out, allowing
  810.    several postfices, with the following syntax (regexp):
  811.  
  812.    [0-9]+       -> bytes
  813.    [0-9]+[kK]   -> bytes * 1024
  814.    [0-9]+[mM]   -> bytes * 1024 * 1024
  815.    inf          -> 0
  816.  
  817.    Anything else is flagged as incorrect, and *out is unchanged. */
  818. int
  819. setbytes(long *out, const char *com, const char *val)
  820. {
  821.    long result;
  822.    const char *p;
  823.  
  824.    result = 0;
  825.    p = val;
  826.    /* Check for "inf". */
  827.    if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0')
  828.    {
  829.       *out = 0L;
  830.       return 1;
  831.    }
  832.    /* Search for digits and construct result. */
  833.    for (; *p && isdigit(*p); p++)
  834.    {
  835.       result *= 10;
  836.       result += *p - '0';
  837.    }
  838.    /* If no digits were found, or more than one character is following
  839.       them, bail out. */
  840.    if (p == val || (*p != '\0' && *(p + 1) != '\0'))
  841.    {
  842.       printf("%s: Invalid specification `%s'\n", com, val);
  843.       return 0;
  844.    }
  845.    /* Search for a designator. */
  846.    switch(*p)
  847.    {
  848.       case 'K': case 'k':
  849.      /* Kilobytes */
  850.      result *= 1024;
  851.      break;
  852.       case 'M': case 'm':
  853.      /* Megabytes */
  854.      result *= (long)1024 * 1024;
  855.      break;
  856.       case '\0':
  857.      /* Never mind. */
  858.      break;
  859.       default:
  860.      printf("%s: Invalid specification `%s'\n", com, val);
  861.      return 0;
  862.    }
  863.    *out = result;
  864.    return 1;
  865. }
  866.